/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __IRENDERER_H__
#define __IRENDERER_H__

#pragma once

#include <vector>
#include <map>

#include "Window.h"
#include "ICamera.h"
#include "ILight.h"
#include "ITexture.h"
#include "IDrawPrimitive.h"


/// Identifier for the available display modes
struct DLL_EXPORT sDisplayMode {
	std::string identifier;
	int width;
	int height;
	int bpp;
	int refreshRate;
};

/**
 * \ingroup CoreEngine
 *
 * The Renderer interface.<br>
 * Provides abstraction to the rendering API.<br>
 * Used for hardware specific rendering functions.
 *
 * \date 03-28-2007
 *
 * \author juckel
 *
 */
class DLL_EXPORT IRenderer
{
public:
	virtual void Destroy();

	/// Creates a new IRenderer object
	static IRenderer* Create( int rendererType );

	// Abstract methods
	
	/// Initialize the renderer for a specified window
	virtual bool init( Window* window ) = 0;
	
	/// Resize the renderer to the specified size
	virtual void resize(int width, int height) = 0;
	
	/// Release the renderer when it's no longer used
	virtual void release() = 0;

	/// Clears the scene with the specified color. Default color is black
	virtual void clearScene(const math::Vector4 &clearColor = math::Vector4(0,0,0,0)) = 0;

	/// Renders the camera. If no camera is specified, the default cam is used
	virtual void drawCamera(ICamera *cam = NULL) = 0;

	/// Pushes the current matrix on the matrix stack
	virtual void pushMatrix() = 0;

	/// Pops the last matrix from the matrix stack
	virtual void popMatrix() = 0;

	/// Saves the current state block. 
	/// \todo Check if this works
	virtual void pushState() = 0;

	/// Restores the last state block. 
	/// \todo Check if this works
	virtual void popState() = 0;

	/// Set up the projection matrix
	virtual void setupProjection(float fov, float near, float far) = 0;

	/// Called at the beginning of the rendering task
	virtual bool beginScene() = 0;

	/// Called at the end of the rendering task
	virtual void endScene() = 0;

	/// Called before rendering the scene. can be used to check for lost buffers
	virtual bool prepareBuffer() = 0;

	/// Displays the content on the screen
	virtual void swapBuffer() = 0;

	/// Applies a new matrix to the current matrix stack
	virtual void applyMatrix(const math::Matrix4 &matrix) = 0;

	/// Sets a ITexture object as the current mouse pointer
	virtual void setMousePointer(ITexture* texture, const math::Vector4& color = math::Vector4(1,1,1,1)) = 0;

	/// Draws the mouse at a specified position
	virtual void drawMouse(int x, int y, bool center = false) = 0;

	/// Loads the identity matrix
	virtual void loadIdentity() = 0;

	/// Translates the current matrix by vec
	virtual void translate(const math::Vector3 &vec) = 0;

	/// Rotates the current matrix by x, y, z
	virtual void rotate(float x, float y, float z) = 0;

	/// Scales the current matrix by x, y, z
	virtual void scale(float x, float y, float z) = 0;

	/// Returns the current view matrix
	virtual const math::Matrix4& getViewMatrix() const = 0;

	/// Returns the current projection matrix
	virtual const math::Matrix4& getProjMatrix() const = 0;

	/// Returns the current world transformation matrix
	virtual const math::Matrix4& getWorldMatrix() const = 0;

	/// Returns the view projection matrix (view * projection)
	virtual math::Matrix4 getViewProjMatrix() const = 0;

	/// Returns the world view projection matrix (world * view * projection)
	virtual math::Matrix4 getWorldViewProjMatrix() const = 0;

	/// Returns the world projection matrix (world * projection)
	virtual math::Matrix4 getWorldProjMatrix() const = 0;

	/// Returns the world view matrix (world * view)
	virtual math::Matrix4 getWorldViewMatrix() const = 0;

	/** Set the target for rendering into a texture. 
	 *  If multiple render targets are accepted, dest can specify the target destination
	 */
	virtual bool bindRenderTarget(ITexture *texture, int dest=0) = 0;

	/// Reset the render target to the back buffer
	virtual bool unbindRenderTarget(int dest=0) = 0;

	/// Draws a full screen quad to display a render target on it, if width or height == -1, the back-buffer size is used
	virtual void drawScreenQuad(int width = -1, int height = -1, int offsetX = 0, int offsetY = 0, float scaleFactor=1.0f, float texScale = 1.0f) = 0;

	/// Draws a rectangle on the screen
	virtual void drawRect(int x, int y, int width, int height, const math::Vector4 &color, bool fill = true) = 0;

	/// Draws a texture as a sprite
	virtual void drawSprite(ITexture* texture, const math::Vector3 &pos, bool transform = false, const math::Vector4& color = math::Vector4(1,1,1,1)) = 0;

	/// Draws a rectangle as a plane in space
	virtual void drawPlane(const math::Vector3 &normal, const float d, const math::Vector2f &dimension) = 0;

	/// Draws a line in space given its from and to vectors
	virtual void drawLine(const math::Vector3 &from, const math::Vector3 &to, const math::Vector4 &color = math::Vector4(1,1,1,1)) = 0;

	/// Draws a vector form the current zero position in the direction of vec
	virtual void drawVector(const math::Vector3 &vec, const math::Vector4 &color);

	/// Draws a sphere with the given radius
	virtual void drawSphere( float radius, const math::Vector4 &color = math::Vector4(1,1,1,1)) = 0;

	/// Draws a box with the given dimensions
	virtual void drawBox(float width, float height, float depth, const math::Vector4 &color = math::Vector4(1,1,1,1)) = 0;

	/// Draws a IDrawPrimitive element on screen
	virtual void drawPrimitive(IDrawPrimitive *) = 0;

	/// Sets the back-buffer size, so other functions can query it (i.e. drawScreenQuad)
	virtual void setBackBufferSize(const math::Vector2i &size) = 0;

	/// Reset the back-buffer size to the size of the screen
	virtual void resetBackBufferSize() = 0;

	/// Returns the size of the backbuffer
	virtual const math::Vector2i& getBackBufferSize() const = 0;

	/// Indicates, if gamma is supported by the device
	virtual bool isGammaSupported() = 0;

	/// Creates a linear gamma ramp and binds it to the device
	virtual void setGammaRamp(float gammaFactor) = 0;

	/// Enables / Disables wireframe
	virtual void setWireframe(bool value) = 0;

	//virtual void setDepthFunction() = 0;

	/// Check for the ATI's "Render to Vertex Buffer" extension
	virtual bool supportsR2VB() = 0;

	/// Check for Multiple Render Targets support (Default is 2)
	virtual bool supportsMRT(int numRT = 2) = 0;

	/// Check if the renderer supports HDR (i.e. float textures)
	virtual bool supportsHDR() = 0;

	/// Check if 16 bit float texture filtering is supported
	virtual bool supportsFP16Filter() = 0;

	/// Check if the graphic hardware has the minimum required shader level
	virtual bool hasMinimumShaderLevel(int majVS, int minVS, int majPS, int minPS) = 0;

	virtual void setClipPlane(IDrawPrimitive *primitive) = 0;

	/// Creates a matrix that is reflecting objects in the specified plane
	virtual const math::Matrix4& createReflectionMatrix(IDrawPrimitive *plane) = 0;

	/// Convert screen space coordinates to a vector in world coordinates and return it
	virtual void mouseToWorld(int x, int y, math::Vector3* outStart, math::Vector3* outDir) = 0;
	
// Methods inside IRenderer
public:
	typedef std::map<std::string, sDisplayMode> displaymode_list;
	/// Returns a list of the available display modes
	const displaymode_list& getAvailableDisplayModes() const;

	/// Returns the current display mode
	const sDisplayMode& getCurrentDisplayMode() const;

	/// Returns the default camera object
	ICamera* getCamera() const;

	/// Returns the currently selected camera object
	ICamera* getCurrentCamera() const;

	sDisplayMode getDisplayMode(const std::string &resolutionStr);

	Window* getWindow() const;
protected:
	IRenderer();
	virtual ~IRenderer();

	Window*	 pWindow;
	ICamera* pCamera;									///< A pointer to the internal camera
	ICamera* pCurrentCam;								///< A pointer to the current camera

	displaymode_list vAvailableDisplayModes;			///< A list of available display modes
	sDisplayMode     currentDisplayMode;				///< The currently selected display mode
	
	float fZNear;
	float fZFar;
	float fFov;
};


/**
 *  Returns a reference to the current renderer object.
 */
DLL_EXPORT
IRenderer* coreGetRenderer();


/**
 *  Returns the renderer type.
 *  \returns The type of the renderer. Either CORE_DIRECTX or CORE_OPENGL or CORE_UNDEFINED
 */
DLL_EXPORT
int coreGetRendererType();


#endif
